﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.IO;
using System.ComponentModel;
using WeiBeeCommon.Core;
using System.Windows.Forms;
using System.Threading;
using System.Linq;
using QunFaExpert.Properties;

namespace QunFaExpert
{
    /// <summary>
    /// Interaction logic for RetweetExpert.xaml
    /// </summary>
    public partial class RetweetExpert : Window
    {
        public RetweetExpert()
        {
            InitializeComponent();
            _bgwAt.WorkerReportsProgress = true;
            _bgwAt.WorkerSupportsCancellation = true;
            _bgwAt.DoWork += new DoWorkEventHandler(_bgwAt_DoWork);
            _bgwAt.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bgwAt_RunWorkerCompleted);
            _bgwAt.ProgressChanged += new ProgressChangedEventHandler(_bgwAt_ProgressChanged);            
        }

        System.Threading.Timer timerCheckAvaiable;
        // Timer oneminuteTimer;
        const int TenMinutes = 10* 60 * 1000;
        List<string> AtList = new List<string>();
        string preAt;
        string afterAt;
        private void buttonAt_Click(object sender, RoutedEventArgs e)
        {
            Microsoft.Win32.OpenFileDialog openDlg = new Microsoft.Win32.OpenFileDialog();
            bool? file = openDlg.ShowDialog();
            if (file.HasValue && file.Value)
            {
                try
                {
                    var reader = ReaderFactory.GetInstance(openDlg.FileName);
                    AtList = reader.GetAtList(AtList);
                    AtList = AtList.Distinct().ToList();
                    System.Windows.MessageBox.Show(string.Format("账号导入成功，一共导入{0}个账号", AtList.Count));
                    labelAtCounter.Content = string.Format("共有{0}待@",AtList.Count);
                    new EntityHelper().SaveAts(AtList);
                    EnableStartAtButton();
                }
                catch
                {
                    System.Windows.MessageBox.Show("账号从文件导入失败，请检查文件格式");
                }
            }
        }

        private readonly BackgroundWorker _bgwAt = new BackgroundWorker();
        private void buttonStartAt_Click(object sender, RoutedEventArgs e)
        {
            StartAt();
        }

        private void StartAt()
        {
            string id = TweeterUrlToId(textBoxRetweet.Text);
            preAt = textBoxPreAt.Text;
            afterAt = textBoxAfterAt.Text;
            _bgwAt.RunWorkerAsync(id as object);
            tbResult.Text = string.Empty;
            buttonCancel.IsEnabled = true;
            buttonStartAt.IsEnabled = false;
            Settings.Default.AtUrl = textBoxRetweet.Text;
            Settings.Default.AfterAt = textBoxAfterAt.Text;
            Settings.Default.PreAt = textBoxPreAt.Text;
            Settings.Default.Save();
            timerCheckAvaiable.Change(Timeout.Infinite, Timeout.Infinite);
        }

        void _bgwAt_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            string progressString = e.UserState as string;
            tbResult.Text += progressString + Environment.NewLine;
        }

        void _bgwAt_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                tbResult.Text += @"取消@";
            }
            else
            {
                tbResult.Text += @"完成@";
            }
            bs.ResetBindings(true);
            entities.Sort();
            new EntityHelper().SaveEntities(entities);
            entities = new EntityHelper().LoadEntities();
            entities.Sort();
            listBoxAllAccounts.ItemsSource = entities;
            new EntityHelper().SaveAts(AtList);
            labelAtCounter.Content = string.Format("共有{0}待@", AtList.Count);
            EnableStartAtButton();
            buttonCancel.IsEnabled = false;

            if (!e.Cancelled)
            {
                timerCheckAvaiable.Change(5000, TenMinutes);
            }
        }

        private void EnableStartAtButton()
        {
            if ( (AtList.Count > 0) && (selected.Count > 0) )
            {
                buttonStartAt.IsEnabled = true;
            }
            else
            {
                buttonStartAt.IsEnabled = false;
            }
        }

        void _bgwAt_DoWork(object sender, DoWorkEventArgs e)
        {
            string id = e.Argument as string;
            
            while (selected.Count > 0 && AtList.Count > 0)
            {
                var item = selected[0];
                // To do: 30 retweet limit per hour. use this hardcord, because there is no good way to get the rate limit from API
                int RetweetTimeLeft = 30;
                WeiBeeSina weibee = new WeiBeeCommon.Core.WeiBeeSina();
                weibee.OAuth = new WeiBeeCommon.Core.OAuthSina();
                weibee.OAuth.ConsumerKey = item.ConsumerKey;
                weibee.OAuth.ConsumerSecret = item.ConsumerSecret;
                weibee.OAuth.Token = item.Token;
                weibee.OAuth.TokenSecret = item.TokenSecret;

                for (int retweetcount = 0; retweetcount < RetweetTimeLeft && AtList.Count > 0; retweetcount++)
                {
                    string atTo = AtList[0];
                    AtList.Remove(atTo);
                    weibee.Retweet(id, string.Format("{0}@{1} {2}",preAt,atTo,afterAt));
                    _bgwAt.ReportProgress(1, string.Format("{0}@{1}", item.user.name, atTo));
                    Thread.Sleep(5*1000);
                    if (_bgwAt.CancellationPending)
                    {
                        e.Cancel = true;
                        return;
                    }
                }
                WeiBeeCommon.DataTypes.RateLimit rl = weibee.ResultToRateLimit(weibee.RateLimitStatus());
                DateTime resettime = StringToDateTime(rl.resettime);
                entities.Find(wb => wb.Token == item.Token).ActTime = resettime;
                selected.Remove(item);
            }
        }

        /// <summary>
        /// WeiBo.com (maybe Unix) return format to C# DateTime
        /// </summary>
        /// <param name="p">Tue Oct 04 22:00:00 +0800 2011</param>
        /// <returns>DateTime</returns>
        public DateTime StringToDateTime(string p)
        {
            DateTime result;
            if ( !DateTime.TryParseExact(p, "ddd MMM dd HH:mm:ss zz00 yyyy", new System.Globalization.CultureInfo("en-US"), System.Globalization.DateTimeStyles.AllowWhiteSpaces, out result))
            {
                result = DateTime.Now;
            }
            return result;
        }

        /// <summary>
        /// 将某一条新浪微博地址转换成这条微博的ID
        /// </summary>
        /// <param name="url">一条微博地址</param>
        /// <returns>微博ID</returns>
        public string TweeterUrlToId(string url)
        {
            url = url.Trim().Substring(url.LastIndexOf("/") + 1);//取得url地址后的62进制数【5KD0TcOcJsZ】
            string[] surl = new string[3];
            surl[2] = String60To10(url.Substring(url.Length - 4));//倒数第三段，取4位，转10进制
            surl[1] = String60To10(url.Substring(url.Length - 8, 4));//倒数第二段，取4位，转10进制
            surl[0] = String60To10(url.Substring(0, url.Length - 8));//第一段，取剩下位数，转10进制
            surl[2] = PadLeftToLengthSeven(surl[2]);
            surl[1] = PadLeftToLengthSeven(surl[1]);
            
            return surl[0] + surl[1] + surl[2];//合并三段，返回
        }

        /// <summary>
        /// 用0补齐7位长度
        /// </summary>
        /// <param name="str">源串可能长度小于7</param>
        /// <returns>返回长度为7的串</returns>
        private string PadLeftToLengthSeven(string str)
        {
            if (str.Length < 7)
            {
                str = str.PadLeft(7, '0');
            }
            return str;
        }

        private string String60To10(string str62)
        {//62进制转10进制
            Int64 i64 = 0;
            for (int i = 0; i < str62.Length; i++)
            {
                Int64 Vi = (Int64)Math.Pow(62, (str62.Length - i - 1));
                char t = str62[i];
                i64 += Vi * String62Dictionary(t.ToString());
            }
            return i64.ToString();
        }
        private Int64 String62Dictionary(string ks)//62进制字典
        {
            string[] keys = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
            int i = 0;
            foreach (string k in keys)
            {
                if (ks == k) { return i; }
                i++;
            }
            return 0;
        }

        private void btManage_Click(object sender, RoutedEventArgs e)
        {
            var manageWeiBeeDlg = new ManageWeiBee();
            bool? showed = manageWeiBeeDlg.ShowDialog();
            if (showed.HasValue)
            {
                InitAccounts();
            }
        }

        List<AuthEntity> entities;
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            InitAccounts();
            timerCheckAvaiable = new System.Threading.Timer(CheckAvaiable, SyncContextCallback(EndCheckAvaiable), Timeout.Infinite, Timeout.Infinite);
        }

        private void InitAccounts()
        {
            entities = new EntityHelper().LoadEntities();
            entities.Sort();
            listBoxAllAccounts.ItemsSource = entities;
            InitSelected();
            AtList = new EntityHelper().LoadAts();
            labelAtCounter.Content = string.Format("共有{0}待@", AtList.Count);
            EnableStartAtButton();
            buttonCancel.IsEnabled = false;
            textBoxPreAt.Text = Settings.Default.PreAt;
            textBoxAfterAt.Text = Settings.Default.AfterAt;
            textBoxRetweet.Text = Settings.Default.AtUrl;
        }
        private void InitSelected()
        {
            selected = new List<AuthEntity>();
            bs.DataSource = selected;
            listBoxSelectAccounts.ItemsSource = bs;
            listBoxSelectAccounts.DisplayMemberPath = "user.name";
            listBoxSelectAccounts.ItemStringFormat = "@{0}";
        }

        public void CheckAvaiable(object endCheckCallback)
        {
            var endCheckAvaiable = endCheckCallback as AsyncCallback;
            if (endCheckAvaiable != null) endCheckAvaiable(null);
        }

        void EndCheckAvaiable(IAsyncResult asyncResult)
        {
            SelectFive();
            if (selected.Count > 0)
            {
                tbResult.Text += @"auto continue..." + Environment.NewLine;
                if (buttonStartAt.IsEnabled)
                {
                    StartAt();
                }
            }
        }

        List<AuthEntity> selected = new List<AuthEntity>();
        BindingSource bs = new BindingSource();
        private void buttonSelectOne_Click(object sender, RoutedEventArgs e)
        {
            var entityToAdd = listBoxAllAccounts.SelectedItem as AuthEntity;
            if (!selected.Contains(entityToAdd))
            {
                selected.Add(entityToAdd);
                bs.ResetBindings(true);
            }
            EnableStartAtButton();
        }

        private void buttonRemoveSelected_Click(object sender, RoutedEventArgs e)
        {
            selected.Remove(listBoxSelectAccounts.SelectedItem as AuthEntity);
            bs.ResetBindings(true);
            EnableStartAtButton();
        }

        private void buttonSelectFive_Click(object sender, RoutedEventArgs e)
        {
            SelectFive();
        }

        private void SelectFive()
        {
            for (int i = 0; i < 5 && i < entities.Count; i++)
            {
                if (!selected.Contains(entities[i]) && entities[i].ActTime <= DateTime.Now)
                {
                    selected.Add(entities[i]);
                }
            }
            bs.ResetBindings(true);
            EnableStartAtButton();
        }

        private void buttonCancel_Click(object sender, RoutedEventArgs e)
        {
            _bgwAt.CancelAsync();
        }

        private static AsyncCallback SyncContextCallback(AsyncCallback callback)
        {
            SynchronizationContext sc = SynchronizationContext.Current;
            if (sc == null) return callback;
            return asyncResult => sc.Post(result => callback((IAsyncResult)result), asyncResult);
        }
    }
}
